/// 使用包、Crate和模块管理不断增长的项目

/*
当你编写大型程序时，组织你的代码显得尤为重要，因为你想在脑海中通晓整个程序，那几乎是不可能完成的。
通过对相关功能进行分组和划分不同功能的代码，你可以清楚在哪里可以找到实现了特定功能的代码，
以及在哪里可以改变一个功能的工作方式。

到目前为止，我们编写的程序都在一个文件的一个模块中。伴随着项目的增长，
你可以通过将代码分解为多个模块和多个文件来组织代码。一个包可以包含多个二进制 crate 项和一个可选的 crate 库。
伴随着包的增长，你可以将包中的部分代码提取出来，做成独立的 crate，这些 crate 则作为外部依赖项。

除了对功能进行分组以外，封装实现细节可以使你更高级地重用代码：你实现了一个操作后，
其他的代码可以通过该代码的公共接口来进行调用，而不需要知道它是如何实现的。
你在编写代码时可以定义哪些部分是其他代码可以使用的公共部分，以及哪些部分是你有权更改实现细节的私有部分。
这是另一种减少你在脑海中记住项目内容数量的方法。

Rust 有许多功能可以让你管理代码的组织，包括哪些内容可以被公开，哪些内容作为私有部分，以及程序每个作用域中的名字。
这些功能。这有时被称为 “模块系统（the module system）”，包括：
    包（Packages）： Cargo 的一个功能，它允许你构建、测试和分享 crate。
    Crates ：一个模块的树形结构，它形成了库或二进制项目。
    模块（Modules）和 use： 允许你控制作用域和路径的私有性。
    路径（path）：一个命名例如结构体、函数或模块等项的方式
*/


/// 包和 crate
/*
crate 是一个二进制项或者库。crate root 是一个源文件，Rust 编译器以它为起始点，并构成你的 crate 的根模块
包（package）是提供一系列功能的一个或者多个 crate。一个包会包含有一个 Cargo.toml 文件，阐述如何去构建这些 crate。

包中所包含的内容由几条规则来确立。一个包中至多 只能 包含一个库 crate（library crate）；
包中可以包含任意多个二进制 crate（binary crate）；包中至少包含一个 crate，无论是库的还是二进制的。
使用命令创建一个包： cargo new my-project

Cargo 遵循的一个约定：src/main.rs 就是一个与包同名的二进制 crate 的 crate 根。
同样的，Cargo 知道如果包目录中包含 src/lib.rs，则包带有与其同名的库 crate，
且 src/lib.rs 是 crate 根。crate 根文件将由 Cargo 传递给 rustc 来实际构建库或者二进制项目。

在此，我们有了一个只包含 src/main.rs 的包，意味着它只含有一个名为 my-project 的二进制 crate。
如果一个包同时含有 src/main.rs 和 src/lib.rs，则它有两个 crate：一个库和一个二进制项，且名字都与包相同。
通过将文件放在 src/bin 目录下，一个包可以拥有多个二进制 crate：每个 src/bin 下的文件都会被编译成一个独立的二进制 crate。
*/


/// 定义模块来控制作用域与私有性
/// src/lib.rs
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}



/// 当我们想使用 use 语句将两个具有相同名称的项带入作用域，因为 Rust 不允许这样做。即
///     use std::fmt::Result;
///     use std::io::Result
/// 是被禁止的，存在同名 Result
fn main() {
    use std::fmt;
    use std::io;

    fn function1() -> fmt::Result {
        // --snip--
        Ok(())
    }

    fn function2() -> io::Result<()> {
        // --snip--
        Ok(())
    }
}


/// 使用 as 关键字提供新的名称
/// 使用 use 将两个同名类型引入同一作用域这个问题还有另一个解决办法：在这个类型的路径后面，我们使用 as 指定一个新的本地名称或者别名。
fn main() {
    use std::fmt::Result;
    use std::io::Result as IoResult;

    fn function1() -> Result {
        // --snip--
        Ok(())
    }

    fn function2() -> IoResult<()> {
        // --snip--
        Ok(())
    }
}


/// 使用 pub use 重导出名称
// 当使用 use 关键字将名称导入作用域时，在新作用域中可用的名称是私有的。
// 如果为了让调用你编写的代码的代码能够像在自己的作用域内引用这些类型，
// 可以结合 pub 和 use。这个技术被称为 “重导出（re-exporting）”，
// 因为这样做将项引入作用域并同时使其可供其他代码引入自己的作用域。



/// 通过 glob 运算符将所有的公有定义引入作用域
use std::collections::*;
// 如果希望将一个路径下 所有 公有项引入作用域，可以指定路径后跟 glob 运算符 *
// 这个 use 语句将 std::collections 中定义的所有公有项引入当前作用域。
// 使用 glob 运算符时请多加小心！Glob 会使得我们难以推导作用域中有什么名称和它们是在何处定义的。